import React, { useEffect, useMemo } from 'react';
import { useImmer } from 'use-immer';
import { createUUID } from '../utils';
import './index.scss';

interface AgGroupProps<T> {
    /* 数据源 */
    dataSource: Array<T>;
    /* 针对删除保留的最小项 */
    min?: number;
    /* 标识每条数据唯一的key，如果不传，默认会给每条数据设置$$id 作为唯一的key */
    keyName?: keyof T;
    /* 每条数据的默认模版 */
    initValue?: T;
    /* 组件的插槽数据 */
    children?: React.ReactNode;
    /* 给最外层容器的style */
    style?: React.CSSProperties;
    /* 最外层容器的className */
    className?: string;
    /* 插槽子元素外层容器的className */
    childrenClassName?: string;
    /* 渲染区外层容器的className */
    sectionClassName?: string;
    /* 渲染数据的函数 */
    render: (taregt: T, key: string, handler: handlerType<T>) => React.ReactNode;
    /* 获取加工处理后的数据 */
    getProcessedData?: (data: Array<T>) => void;
}

interface handlerType<T> {
    add: (id?: string, payload?: any) => void;
    remove: (id: string) => void;
    copy: (id: string) => any;
    notify: (id: string, callback: (target: T) => void) => void;
}

const AgGroup = <T, R = any>(props: AgGroupProps<T>) => {
    const {
        min = 1,
        keyName,
        initValue,
        dataSource,
        children,
        style,
        className = '',
        childrenClassName = '',
        sectionClassName = '',
        render,
        getProcessedData,
    } = props;

    const [list, setList] = useImmer<Array<T>>([]);
    const key: string = useMemo(() => (keyName as string) || '$$id', [keyName]);

    const dataSourceIsChanged = useMemo(() => {
        return dataSource.reduce((pre, item: any) => `${item[key]},${pre}`, '');
    }, [dataSource]);

    useEffect(() => {
        const initDataSource: any = dataSource.length ? dataSource : [createInitValue()];

        if (keyName) {
            setList(initDataSource);
            return;
        }

        const dataList = initDataSource.map((item: any) => {
            return {
                $$id: createUUID(),
                ...item,
            };
        });

        setList(dataList);
    }, [dataSourceIsChanged]);

    useEffect(() => {
        if (!getProcessedData || !list.length) return;

        getProcessedData(list);
    }, [list]);

    const createId = () => ({ [key]: createUUID() });

    const createInitValue = () => {
        const initData = initValue || {};

        return {
            ...initData,
            ...createId(),
        };
    };

    const add = (id?: string, payload?: Record<string, any>) => {
        let initData: any = {};
        if (payload) {
            initData = {
                ...payload,
                ...createId(),
            };
        } else {
            initData = createInitValue();
        }

        if (!id) {
            setList((draft) => {
                draft?.push(initData);
            });
            return;
        }

        const index = list.findIndex((item: any) => item[key] === id);
        setList((draft) => {
            draft?.splice((index as number) + 1, 0, initData);
        });
    };

    const remove = (id: string) => {
        const index = list.findIndex((item: any) => item[key] === id);
        setList((draft) => {
            if ((draft?.length as number) <= min) {
                const initData: any = createInitValue();
                draft?.splice(index as number, 1, initData);
            } else {
                draft?.splice(index as number, 1);
            }
        });
    };

    const copy = (id: string) => {
        const index = list.findIndex((item: any) => item[key] === id);
        const copyData: any = {
            ...list[index],
            ...createId(),
        };

        setList((draft) => {
            draft?.splice((index as number) + 1, 0, copyData);
        });
    };

    const notify = (id: string, callback: (target: T) => void) => {
        setList((draft) => {
            const current = draft?.find((item: any) => item[key] === id);
            callback(current as T);
        });
    };

    return (
        <main
            style={style}
            className={`ag-group ${className}`}
        >
            <section className={`ag-group-children ${childrenClassName}`}>{children}</section>
            <section>
                {render &&
                    list.map((target: any) => {
                        const id = target[key];

                        return (
                            <div
                                className={`ag-group-section ${sectionClassName}`}
                                key={id}
                            >
                                {render(target, id, {
                                    add,
                                    remove,
                                    copy,
                                    notify,
                                })}
                            </div>
                        );
                    })}
            </section>
        </main>
    );
};

export default AgGroup;
